// SCORELIGHT based on Arduino MEGA
// New version using OSC over ETHERNET
// April 30 2011, Alvaro Cassinelli

/* 
  NOTES:
  - IMPORTANT: The ethernet shield uses pin 10 to select the W5100 chip, and pin4 for the SD card, so it should be ok to use the same clock, miso, mosi pins for the SPI.. but the problem
    is that the Ethernet routines may use interruptions, and then interrupt the output to the mirror DAC!!! perhaps it would be better to set the SPI pins for the MCP4922 DAC on other pins. 
*/


//OSC related (could be in hardwareIO) ================================================
#include <SPI.h>
#include "Ethernet.h"
#include "OSCClass.h"
// (a) THIS ARDUINO IS THE SERVER:
byte serverMac[] = {0x90, 0xA2, 0xDA, 0x00, 0x20, 0xFF };
byte serverIp[]  = { 10, 0, 0, 2 };
int  serverPort  = 10000;
//  byte gateway[]   = { 192, 168, 0, 1 };
//  byte subnet[]    = { 255, 255, 255, 0 };

  // (b) SET THE ADDRESS OF THE CLIENT, i.e., Hirakawa's computer (is it needed? cannot we just reply?)
  byte destIp[]  = { 10,0,0,1}; // 
  int  destPort = 12000;
  
  
  // OSC message objects (for receveing and answering): 
  OSCMessage recMes;
  OSCMessage sendMes;
  
  // set the receving message that will be filled when receiving an osc packet: 
  OSCClass osc(&recMes);
// =============================================================================

//============= LIBRARIES ====================================================
#include "classMidi.h" // custom midi library, with basic midi (note on, off, program change, as well as more complex functions)
#include "hardwareIO.h" // rem: an extern SPI object is declared here, to be used both by the main program and the LivingSpot class
#include "soundSpot.h"  // this is a derived class of livingSpot, with sending capabilities (for sound). 

// PIN to MANUALLY change modes (DIGITAL)
#define pinSwitch 2 

//potentiometer to set the FIXED threshold (ANALOG pin)
#define pinAnalogPot 1  
int potValue;
int potFixThreshold;

//#define SCHEDULERMODETIME 1200000//40000// 200000 // in milliseconds (REM: VERY LONG FOR SEKAI ICHI UKETAI JYUGYOU! (1200000=20 minutes...)
unsigned long SCHEDULERMODETIME = 5000;//modif Alexis time change for each mode
unsigned long schedulerTime;
int schedulerMode;

/// ALWAYS instantiate 3 or 4 SPOTS (but they don't need to be "alive" at the same time)
/// midi and scheduler functions are using 4 spots
#define NBSPOT 4
soundSpot spotArray[NBSPOT]; // Attention: here the default constructor is called. It should not use any arduino function relating to hardware that has not been initialized (before the setup function). Instead, use an "init" method. 
 
 
void setup(){
 
  // ===================  ETHERNET SETUP ===================
  // Important: il faut le faire AVANT d'utiliser le SPI (si on utilise la librarie SPI, car l'initialisation est dans le begin de Ethernet. Si on utilise une custom spi class, we need to call its init method!!!)
    // SET ETHERNET ADDRESS (IP and MAC):
     Ethernet.begin(serverMac ,serverIp);
    //Ethernet.begin(serverMac ,serverIp ,gateway ,subnet);
    
     //setting osc recieve server on specific PORT:
     osc.begin(serverPort);
   
     // set destination messages: 
      sendMes.setIp( destIp );
      sendMes.setPort( destPort );
      //sendMes.setTopAddress(topAddress); // topAddress="/ard"
      //sendMes.setSubAddress(subAddress[0]);
      
     //osc message buffer clear
     osc.flush();
     //==========================================================
     
        myMidi.init();// myMidi has been declared extern, so it is visible to functions in other files
        
       IO.init();  // set all the IO functions (PWM generation, SPI initialization, etc).
        // All other IO methods can be called AFTER we called the IO.init

  // for tests:
   // pinMode(31, OUTPUT); digitalWrite(31,HIGH); delay(200); digitalWrite(31, LOW); delay(200); digitalWrite(31,HIGH); delay(200); digitalWrite(31, LOW);   delay(200); digitalWrite(31,HIGH); 
 
 
  pinMode(pinSwitch, INPUT);
  digitalWrite(pinSwitch,HIGH);// to activate pullup resistors (i.e., when switch is off, the pin reads HIGH). 

 // Potentiometer:
// ATTN: normally there is no need to set the inputs as INPUTS, unless there were configured otherwise 
  //("The analogRead command will not work correctly if a pin has been previously set to an output,"
   // For security, we do it here (actually, wihout doing this, I had a problem!). BEWARE: we need to use A0, A1, A2, not 0, 1 and 2..
  pinMode(A1, INPUT); // corresponds to analog input pin 1 (pinAnalogPot)
  digitalWrite(A1, LOW); // reset pullup resistors

  IO.setGreenPower(HIGH);
  IO.setRedPower_mW(MAX_RedLaserPower);
  
  // ===================  SERIAL SETUP on Serial port ===================
  // Setup serial communication with the computer:
  Serial.begin(115200);
  
 // SCHEDULER INITIALIZATION or all spots initialization but all "dead" for control through OSC:
 // schedulerInit();
 allSpotsInit();

  Serial.print("Arduino Server ready.");
}


// For tests angle:


/*
void loop(){
// mesure angle:
  // spotArray[0].measureAngle();

// measure limits:
        Spi.writeOutR(0);
        spotArray[0].drawMaxArea(1);
        digitalWrite(greenLaserPin, LOW);
        spotArray[0].drawMaxArea(10);
}
*/


void loop(){
  
   // PROCESSING INPUT COMMANDS:
   if ( osc.available() ) processOSC(); // this is basically to change the modes, etc. 
   if ( Serial.available() ) processSerial(); 
   // NOTE: we assume that MIDI is not sending commands, but could... (on Serial1)
   
   // AUTO SENDING MODES:
    
  
  // pin mode for MANUALLY changing modes: 
  if (digitalRead(pinSwitch)==LOW) {
    delay(200); // simple way to avoid switch bouncing
    schedulerTime = millis()-SCHEDULERMODETIME; // this will force the scheduler to change the mode
   // Serial.println("button pressed");
  }
  

  // for tests on scanning area:
   if(schedulerMode==0) {
          Spimcp.writeOutR(0);
          spotArray[0].drawMaxArea(1);
          digitalWrite(greenLaserPin, LOW);
          spotArray[0].drawMaxArea(1);
          digitalWrite(greenLaserPin, HIGH);
          Spimcp.writeOutR(4000);
        }
 
  
  for(int i=0; i<NBSPOT; i++){
    if(spotArray[i].alive){
      
       // SENDING DATA if required:
      // Send data (over serial and/or ethernet and/or midi (Serial1) depending on the sending mode:
      spotArray[i].sendData();
    
      // update threshold?
       if(spotArray[i].behaviourMode == SNELL_LAW){
       //continuous polling of threshold from potentiomenter:
       potentiometerThresholdRead();
       spotArray[i].updateThreshold(potFixThreshold);
       }
      
      // update dynamics:
      spotArray[i].update(spotArray, NBSPOT);
      
     // change radius?
    if(spotArray[i].behaviourMode == SIMPLE_BOUNCING){ 
        byte radius = 5*sin(200 * (millis()-schedulerTime) / 15000.0+3.1415*i)+18;
        spotArray[i].setRadiusSaccade( radius );
      }
      
      
/*
      if(spotArray[i].behaviourMode == COUNTOUR_FOLLOWING){ 
        midiPitchAngle(i, spotArray[i].onContour, spotArray[i].target_angle);
        //midiPitchBendAngle(i, spotArray[i].onContour, spotArray[i].target_angle);

        // change size?:
       // if (i==1) {
       // byte radius = 5*sin(200 * (millis()-schedulerTime) / 10000.0)+35;;//15*sin(200 * (millis()-schedulerTime) / 10000.0)+45;
       // spotArray[1].setRadiusSaccade( radius );
       //  if (radius>55) midiCollisionPercussion(i);//midiCollision(i, 0);//
       // }
      }
*/
         
    }    
  }
  
  scheduler();
}

// POTENTIOMETER:
void potentiometerThresholdRead() {
 potValue=analogRead(pinAnalogPot); // value between 0 and 1023
 // set the corresponding potentiometer based fixed threshold:
 potFixThreshold=1.0*potValue/1023*255; // value between 0 and 255 (the readout of the custom lockin is 8 bytes)
 //Serial.println(potFixThreshold);
}




